package jmine.tec.hydra.dao.filter;

import java.util.ArrayList;
import java.util.List;

import jmine.tec.hydra.api.HydraSpecification;
import jmine.tec.hydra.domain.HydraSystem;
import jmine.tec.hydra.domain.filter.HydraFilter;
import jmine.tec.hydra.domain.filter.HydraFilterType;

import org.apache.commons.collections.list.SetUniqueList;
import org.hibernate.Criteria;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;

import bancosys.tec.persist.dao.BaseDAO;
import bancosys.tec.persist.hibernate.RestrictionsUtils;

/**
 * DAO de filtros de integração.
 * 
 * @author piercio
 */
public class HydraFilterDAO extends BaseDAO<HydraFilter> {

    /**
     * Primeiramente busca pelos filtros que estão associados a ação e sistema da especificação informada. Depois busca pelos filtros que
     * estão associados ao sistema da especificação. Retorna uma lista que é a união das duas listas, sem elementos duplicados.
     * 
     * @param specification especificação.
     * @return lista de filtros.
     */
    @SuppressWarnings("unchecked")
    public List<HydraFilter> findSendingBySpecification(HydraSpecification specification) {
        List<HydraFilter> filtersByActionSystem = this.findSendingByActionSystem(specification.getAction(), specification.getSystem());
        List<HydraFilter> filtersBySystem = this.findSendingBySystem(specification.getSystem());

        List<HydraFilter> allFilters = new ArrayList<HydraFilter>();
        allFilters.addAll(filtersByActionSystem);
        allFilters.addAll(filtersBySystem);
        return SetUniqueList.decorate(allFilters);
    }

    /**
     * Primeiramente busca pelos filtros que estão associados a ação e sistema da especificação informada. Depois busca pelos filtros que
     * estão associados ao sistema da especificação. Retorna uma lista que é a união das duas listas, sem elementos duplicados.
     * 
     * @param specification especificação.
     * @return lista de filtros.
     */
    @SuppressWarnings("unchecked")
    public List<HydraFilter> findReceivingBySpecification(HydraSpecification specification) {
        List<HydraFilter> filtersByActionSystem = this.findReceivingByActionSystem(specification.getAction(), specification.getSystem());
        List<HydraFilter> filtersBySystem = this.findReceivingBySystem(specification.getSystem());

        List<HydraFilter> allFilters = new ArrayList<HydraFilter>();
        allFilters.addAll(filtersByActionSystem);
        allFilters.addAll(filtersBySystem);
        return SetUniqueList.decorate(allFilters);
    }

    /**
     * Devolve todos os filtros de envio de um dado sistema.
     * 
     * @param system o sistema.
     * @return todas os filtros de envio para um dado sistema.
     */
    public List<HydraFilter> findSendingBySystem(HydraSystem system) {
        return this.doFindBySystem(null, system, HydraFilterType.SENDING);
    }

    /**
     * Devolve todos os filtros de envio de um dado sistema com uma dada ação.
     * 
     * @param action a ação.
     * @param system o sistema.
     * @return todas os filtros de envio para um dado sistema.
     */
    public List<HydraFilter> findSendingByActionSystem(String action, HydraSystem system) {
        return this.doFindBySystem(action, system, HydraFilterType.SENDING);
    }

    /**
     * Devolve todos os filtros de recebimento de um dado sistema.
     * 
     * @param system o sistema.
     * @return todas os filtros de recebimento para um dado sistema.
     */
    public List<HydraFilter> findReceivingBySystem(HydraSystem system) {
        return this.doFindBySystem(null, system, HydraFilterType.RECEIVING);
    }

    /**
     * Devolve todos os filtros de recebimento de um dado sistema com uma dada ação.
     * 
     * @param action a ação.
     * @param system o sistema.
     * @return todas os filtros de recebimento para um dado sistema.
     */
    public List<HydraFilter> findReceivingByActionSystem(String action, HydraSystem system) {
        return this.doFindBySystem(action, system, HydraFilterType.RECEIVING);
    }

    /**
     * Efetua a busca por ação, sistema e tipo de filtro.
     * 
     * @param action a ação.
     * @param system o sistema.
     * @param type o tipo de filtro.
     * @return todos os filtros de um dado tipo, para um dado sistema.
     */
    private List<HydraFilter> doFindBySystem(String action, HydraSystem system, HydraFilterType type) {
        Criteria crit = this.createCriteria();
        if (action != null) {
            crit.add(Restrictions.eq("action", action));
        } else {
            crit.add(Restrictions.isNull("action"));
        }
        crit.add(Restrictions.eq("system", system));
        crit.add(Restrictions.eq("filterType", type));
        crit.add(Restrictions.eq("active", Boolean.TRUE));
        return this.executeQuery(crit);
    }

    /**
     * Busca todos os filtros do sistema
     * 
     * @param system system
     * @return Lista de HydraActivation
     */
    public List<HydraFilter> findBySystem(HydraSystem system) {
        Criteria criteria = this.createCriteria();
        RestrictionsUtils.addRestrictionEq(criteria, "system", system);
        criteria.addOrder(Order.asc("id"));
        return this.executeQuery(criteria);
    }
}
